%error-verbose

%{
#include <stdio.h>
#include <string>
#include <list>
#include "library/node.h"
#include "library/operand.h"
#include "library/operatorunary.h"
#include "library/operatorbinary.h"
#include "library/variable.h"
#include "library/vector.h"
#include "library/assignment.h"
#include "library/call.h"
#include "library/instance.h"
#include "library/basetype.h"
#include "library/return.h"
#include "library/declarationvariable.h"
#include "library/block.h"
#include "library/conditional.h"
#include "library/loop.h"
#include "library/declarationfunction.h"
#include "library/program.h"
#include "library/type.h"
extern int yylineno;
extern int yylex();
extern char *yytext;
void yyerror(const char *content)
{
    fprintf(stderr, "line %d error: %s \"%s\"\n", yylineno, content, yytext);
}
// Variável Global Externa
AST::Program* program;
%}

%union
{
    AST::Node* node;
    std::string* text;
    std::list<AST::Node*>* container;
}

%type <node> expression
%type <node> variable
%type <node> command
%type <node> call
%type <node> vector
%type <node> type
%type <node> basetype
%type <node> block
%type <node> else
%type <node> declaration_function
%type <node> program
%type <container> parameters
%type <container> names
%type <container> declaration_variable
%type <container> declaration_variables
%type <container> commands
%type <container> attributes
%type <container> declarations

%token <text> T_INT
%token <text> T_FLOAT
%token <text> T_CHAR
%token <text> T_VOID
%token T_NEW
%token T_IF
%token T_ELSE
%token T_ENDIF
%token T_WHILE
%token T_RETURN
%token T_ASSIGN
%token T_COMMA
%token T_SEMICOLON
%token <text> T_EQ
%token <text> T_LE
%token <text> T_GE
%token <text> T_LT
%token <text> T_GT
%token <text> T_AND
%token <text> T_OR
%token T_OPB
%token T_CPB
%token T_OVB
%token T_CVB
%token T_OSB;
%token T_CSB;
%token <text> T_SUB
%token <text> T_ADD
%token <text> T_MUL
%token <text> T_DIV
%token <text> T_NOT
%token <text> T_NFLOAT
%token <text> T_NINTEGER
%token <text> T_LITERAL
%token <text> T_IDENTIFIER;
%token T_UNKNOWN

%left T_EQ T_LE T_GE T_LT T_GT
%left T_AND T_OR
%left T_ADD T_SUB
%left T_MUL T_DIV
%left T_NOT

%nonassoc T_ENDIF
%nonassoc T_ELSE

%start program

%%

program
    : /* empty */  { program = new AST::Program() }
    | declarations { program = new AST::Program($1); }
    ;

declarations
    : declaration_variable
        { $$ = $1 }
    | declaration_function
        {
            $$ = new std::list<AST::Node*>();
            $$->push_front($1);
        }
    | declaration_variable declarations
        {
            $$ = $2;
            for (std::list<AST::Node*>::iterator i = $1->begin(); i != $1->end(); i++) {
                $$->push_front(*i);
            }
        }
    | declaration_function declarations
        { $$ = $2; $$->push_front($1); }
    ;

declaration_function
    : type T_IDENTIFIER T_OPB T_CPB block
        { $$ = new AST::DeclarationFunction($2, $5, $1) }
    | type T_IDENTIFIER T_OPB attributes T_CPB block
        { $$ = new AST::DeclarationFunction($2, $6, $4, $1) }
    | T_VOID T_IDENTIFIER T_OPB T_CPB block
        { $$ = new AST::DeclarationFunction($2, $5) }
    | T_VOID T_IDENTIFIER T_OPB attributes T_CPB block
        { $$ = new AST::DeclarationFunction($2, $6, $4) }
    ;

attributes
    : type T_IDENTIFIER
        {
            $$ = new std::list<AST::Node*>();
            $$->push_front(new AST::DeclarationVariable($2, $1));
        }
    | type T_IDENTIFIER T_COMMA attributes
        {
            $$ = $4;
            $$->push_front(new AST::DeclarationVariable($2, $1));
        }
    ;

declaration_variables
    : /* empty */ { $$ = new std::list<AST::Node*>() }
    | declaration_variable declaration_variables
        {
            $$ = $2;
            for (std::list<AST::Node*>::iterator i = $1->begin(); i != $1->end(); i++) {
                $$->push_front(*i);
            }
        }
    ;

declaration_variable
    : type names T_SEMICOLON
        {
            for (std::list<AST::Node*>::iterator i = $2->begin(); i != $2->end(); i++) {
                ((AST::DeclarationVariable*) (*i))->setType($1);
            }
            $$ = $2;
        }
    ;

type
    : basetype { $$ = $1 }
    | type T_OVB T_CVB { $$ = $1; ((AST::Basetype*) $$)->addDimensions(1) }
    ;

names
    : T_IDENTIFIER
        {
            $$ = new std::list<AST::Node*>();
            $$->push_front(new AST::DeclarationVariable($1));
        }
    | T_IDENTIFIER T_COMMA names
        {
            $$ = $3;
            $$->push_front(new AST::DeclarationVariable($1));
        }
    ;

commands
    : /* empty */ { $$ = new std::list<AST::Node*>() }
    | command commands { $$ = $2; $$->push_front($1) }
    ;

command
    : T_IF T_OPB expression T_CPB command else
        { $$ = new AST::Conditional($3, $5, $6) }
    | T_WHILE T_OPB expression T_CPB command
        { $$ = new AST::Loop($3, $5) }
    | variable T_ASSIGN expression T_SEMICOLON
        { $$ = new AST::Assignment($1, $3) }
    | T_RETURN T_SEMICOLON
        { $$ = new AST::Return() }
    | T_RETURN expression T_SEMICOLON
        { $$ = new AST::Return($2) }
    | call T_SEMICOLON
        { $$ = $1 }
    | block
        { $$ = $1 }
    ;

block
    : T_OSB declaration_variables commands T_CSB { $$ = new AST::Block($2, $3) }
    ;

else
    : %prec T_ENDIF  { $$ = NULL }
    | T_ELSE command { $$ = $2 }
    ;

expression
    : T_NFLOAT
        {
            AST::Operand* operand = new AST::Operand($1);
            operand->setType(new AST::Type(AST::Type::FLOAT));
            $$ = operand;
        }
    | T_NINTEGER
        {
            AST::Operand* operand = new AST::Operand($1);
            operand->setType(new AST::Type(AST::Type::INTEGER));
            $$ = operand;
        }
    | T_LITERAL
        {
            AST::Operand* operand = new AST::Operand($1);
            operand->setType(new AST::Type(AST::Type::CHAR, true));
            $$ = operand;
        }
    | variable                    { $$ = $1 }
    | call                        { $$ = $1 }
    | T_NEW vector                { $$ = $2 }
    | T_OPB expression T_CPB      { $$ = $2 }
    | T_NOT expression            { $$ = new AST::OperatorUnary($1, $2) }
    | T_SUB expression            { $$ = new AST::OperatorUnary($1, $2) }
    | expression T_EQ expression  { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_LE expression  { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_GE expression  { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_LT expression  { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_GT expression  { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_AND expression { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_OR  expression { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_ADD expression { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_SUB expression { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_MUL expression { $$ = new AST::OperatorBinary($2, $1, $3) }
    | expression T_DIV expression { $$ = new AST::OperatorBinary($2, $1, $3) }
    ;

variable
    : T_IDENTIFIER
        { $$ = new AST::Variable($1) }
    | variable T_OVB expression T_CVB
        { $$ = new AST::Vector($1, $3) }
    ;

call
    : T_IDENTIFIER T_OPB T_CPB
        { $$ = new AST::Call($1) }
    | T_IDENTIFIER T_OPB parameters T_CPB
        { $$ = new AST::Call($1, $3) }
    ;

parameters
    : expression
        { $$ = new std::list<AST::Node*>(); $$->push_front($1) }
    | expression T_COMMA parameters
        { $$ = $3; $$->push_front($1) }
    ;

vector
    : basetype T_OVB expression T_CVB
        { $$ = new AST::Instance($1, $3) }
    | vector T_OVB expression T_CVB
        { $$ = new AST::Instance($1, $3) }
    ;

basetype
    : T_INT   { $$ = new AST::Basetype($1) }
    | T_FLOAT { $$ = new AST::Basetype($1) }
    | T_CHAR  { $$ = new AST::Basetype($1) }
    ;

%%

